home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / hplip / ui4 / ui_utils.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  17.0 KB  |  505 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # Std Lib
  23. import os.path
  24. import re
  25. import os
  26.  
  27. # Local
  28. from base.g import *
  29. from base.codes import *
  30. from base import utils
  31.  
  32. # Qt
  33. from PyQt4.QtCore import *
  34. from PyQt4.QtGui import *
  35.  
  36. pat_html_remove = re.compile("(?is)<.*?>", re.I)
  37.  
  38. # databaseChanged signal values (for FABWindow)
  39. FAB_NAME_ADD = 0  # s1 - new name
  40. FAB_NAME_RENAME = 1 # s1 - old name, s2 - new name
  41. FAB_NAME_REMOVE = 2 # s1 - removed name
  42. FAB_NAME_DETAILS_CHANGED = 3 # s1 - name
  43. FAB_GROUP_ADD = 4 # s1 - new group
  44. FAB_GROUP_RENAME = 5 # s1 - old group, s2 - new group
  45. FAB_GROUP_REMOVE = 6 # s1 - removed group
  46. FAB_GROUP_MEMBERSHIP_CHANGED = 7 # s1 - group
  47.  
  48.  
  49. def __translate(t):
  50.     return QApplication.translate("ui_utils", t, None, QApplication.UnicodeUTF8)
  51.  
  52.  
  53. def beginWaitCursor():
  54.     QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
  55.  
  56.  
  57. def endWaitCursor():
  58.     QApplication.restoreOverrideCursor()
  59.  
  60.  
  61. # TODO: Cache pixmaps
  62. def load_pixmap(name, subdir=None, resize_to=None):
  63.     name = ''.join([os.path.splitext(name)[0], '.png'])
  64.  
  65.     if subdir is None:
  66.         dir = prop.image_dir
  67.         ldir = os.path.join(os.getcwd(), 'data', 'images')
  68.     else:
  69.         dir = os.path.join(prop.image_dir, subdir)
  70.         ldir = os.path.join(os.getcwd(), 'data', 'images', subdir)
  71.  
  72.     for d in [dir, ldir]:
  73.         f = os.path.join(d, name)
  74.         if os.path.exists(f):
  75.             if resize_to is not None:
  76.                 img = QImage(f)
  77.                 x, y = resize_to
  78.                 return QPixmap.fromImage(img.scaled(x, y, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
  79.             else:
  80.                 return QPixmap(f)
  81.  
  82.         for w in utils.walkFiles(dir, recurse=True, abs_paths=True, return_folders=False, pattern=name):
  83.             if resize_to is not None:
  84.                 img = QImage(w)
  85.                 x, y = resize_to
  86.                 return QPixmap.fromImage(img.scaled(x, y, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
  87.             else:
  88.                 return QPixmap(w)
  89.  
  90.     log.error("Pixmap '%s' not found!" % name)
  91.     return QPixmap()
  92.  
  93. loadPixmap = load_pixmap
  94.  
  95.  
  96. class UserSettings(QSettings):
  97.     def __init__(self):
  98.         QSettings.__init__(self, os.path.join(prop.user_dir,  'hplip.conf'),  QSettings.IniFormat)
  99.         self.systray_visible = SYSTRAY_VISIBLE_SHOW_ALWAYS
  100.         self.last_used_device_uri = ''
  101.         self.last_used_printer = ''
  102.         self.version = ''
  103.         self.date_time = ''
  104.         self.auto_refresh = False
  105.         self.auto_refresh_rate = 30
  106.         self.auto_refresh_type = 1
  107.         self.polling_interval = 5
  108.         self.polling = True
  109.         self.device_list = []
  110.         self.loadDefaults()
  111.  
  112.  
  113.     def __setup(self,  cmds):
  114.         for c in cmds:
  115.             basename = c.split()[0]
  116.             path = utils.which(basename)
  117.             if path:
  118.                 return ' '.join([os.path.join(path, basename), ' '.join(c.split()[1:])])
  119.  
  120.         return ''
  121.  
  122.     def loadDefaults(self):
  123.         self.cmd_scan = self.__setup(['xsane -V %SANE_URI%', 'kooka', 'xscanimage'])
  124.         self.cmd_fab = self.__setup(['hp-fab'])
  125.  
  126.  
  127.     def load(self):
  128.         log.debug("Loading user settings...")
  129.         self.sync()
  130.  
  131.         self.beginGroup("settings")
  132.         i, ok = self.value("systray_visible").toInt()
  133.         if ok:
  134.             self.systray_visible = i
  135.  
  136.         self.endGroup()
  137.  
  138.         self.beginGroup("last_used")
  139.         self.last_used_device_uri = unicode(self.value("device_uri").toString()) or self.last_used_device_uri
  140.         self.last_used_printer = unicode(self.value("printer").toString()) or self.last_used_printer
  141.         self.endGroup()
  142.  
  143.         self.beginGroup("commands")
  144.         self.cmd_scan = unicode(self.value("scan").toString()) or self.cmd_scan
  145.         self.endGroup()
  146.  
  147.         self.beginGroup("refresh")
  148.         self.auto_refresh_rate = int(self.value("rate").toString() or self.auto_refresh_rate)
  149.         self.auto_refresh = bool(self.value("enable").toBool())
  150.         self.auto_refresh_type = int(self.value("type").toString() or self.auto_refresh_type)
  151.         self.endGroup()
  152.  
  153.         self.beginGroup("installation")
  154.         self.version = unicode(self.value("version").toString())
  155.         self.date_time = unicode(self.value("date_time").toString())
  156.         self.endGroup()
  157.  
  158.         self.beginGroup("polling")
  159.         self.polling = bool(self.value("enable").toBool())
  160.         self.polling_interval = int(self.value("interval").toString() or self.polling_interval)
  161.         self.polling_device_list = unicode(self.value("device_list").toString() or '').split(u',')
  162.         self.endGroup()
  163.  
  164.  
  165.     def save(self):
  166.         log.debug("Saving user settings...")
  167.  
  168.         self.beginGroup("settings")
  169.         i = QVariant(self.systray_visible)
  170.         self.setValue("systray_visible", QVariant(self.systray_visible))
  171.         self.endGroup()
  172.  
  173.         self.beginGroup("last_used")
  174.         self.setValue("device_uri",  QVariant(self.last_used_device_uri))
  175.         self.setValue("printer", QVariant(self.last_used_printer))
  176.         self.endGroup()
  177.  
  178.         self.beginGroup("commands")
  179.         self.setValue("scan",  QVariant(self.cmd_scan))
  180.         self.endGroup()
  181.  
  182.         self.beginGroup("refresh")
  183.         self.setValue("rate", QVariant(self.auto_refresh_rate))
  184.         self.setValue("enable", QVariant(self.auto_refresh))
  185.         self.setValue("type", QVariant(self.auto_refresh_type))
  186.         self.endGroup()
  187.  
  188.         self.beginGroup("polling")
  189.         self.setValue("enable", QVariant(self.polling))
  190.         self.setValue("interval", QVariant(self.polling_interval))
  191.         self.setValue("device_list", QVariant(u','.join(self.polling_device_list)))
  192.         self.endGroup()
  193.  
  194.         self.sync()
  195.  
  196.  
  197.     def debug(self):
  198.         log.debug("FAB command: %s" % self.cmd_fab)
  199.         log.debug("Scan command: %s" % self.cmd_scan)
  200.         log.debug("Auto refresh: %s" % self.auto_refresh)
  201.         log.debug("Auto refresh rate: %s" % self.auto_refresh_rate)
  202.         log.debug("Auto refresh type: %s" % self.auto_refresh_type)
  203.         log.debug("Systray visible: %d" % self.systray_visible)
  204.         log.debug("Last used device URI: %s" % self.last_used_device_uri)
  205.         log.debug("Last used printer: %s" % self.last_used_printer)
  206.  
  207.  
  208. def su_sudo():
  209.     su_sudo_str = None
  210.  
  211.     if utils.which('kdesu'):
  212.         su_sudo_str = 'kdesu -- %s'
  213.  
  214.     elif utils.which('kdesudo'):
  215.         su_sudo = 'kdesudo -- %s'
  216.  
  217.     elif utils.which('gnomesu'):
  218.         su_sudo_str = 'gnomesu -c "%s"'
  219.  
  220.     elif utils.which('gksu'):
  221.         su_sudo_str = 'gksu "%s"'
  222.  
  223.     return su_sudo_str
  224.  
  225.  
  226.  
  227. DEFAULT_TITLE =  __translate("HP Device Manager")
  228.  
  229.  
  230. def FailureUI(parent, error_text, title_text=None):
  231.     log.error(pat_html_remove.sub(' ', unicode(error_text)))
  232.  
  233.     if title_text is None:
  234.         if parent is not None:
  235.             title_text = parent.windowTitle()
  236.         else:
  237.             title_text = DEFAULT_TITLE
  238.  
  239.     QMessageBox.critical(parent,
  240.         title_text,
  241.         error_text,
  242.         QMessageBox.Ok,
  243.         QMessageBox.NoButton,
  244.         QMessageBox.NoButton)
  245.  
  246. showFailureUi = FailureUI
  247.  
  248.  
  249. def WarningUI(parent,  warn_text, title_text=None):
  250.     log.warn(pat_html_remove.sub(' ', unicode(warn_text)))
  251.  
  252.     if title_text is None:
  253.         if parent is not None:
  254.             title_text = parent.windowTitle()
  255.         else:
  256.             title_text = DEFAULT_TITLE
  257.  
  258.  
  259.     QMessageBox.warning(parent,
  260.         title_text,
  261.         warn_text,
  262.         QMessageBox.Ok,
  263.         QMessageBox.NoButton,
  264.         QMessageBox.NoButton)
  265.  
  266. showWarningUi = WarningUI
  267.  
  268.  
  269. def SuccessUI(parent, text, title_text=None):
  270.     log.info(pat_html_remove.sub(' ', unicode(text)))
  271.  
  272.     if title_text is None:
  273.         if parent is not None:
  274.             title_text = parent.windowTitle()
  275.         else:
  276.             title_text = DEFAULT_TITLE
  277.  
  278.  
  279.     QMessageBox.information(parent,
  280.         title_text,
  281.         text,
  282.         QMessageBox.Ok,
  283.         QMessageBox.NoButton,
  284.         QMessageBox.NoButton)
  285.  
  286. showSuccessUi = SuccessUI
  287.  
  288.  
  289. def CheckDeviceUI(parent, title_text=None):
  290.     text = __translate("<b>Unable to communicate with device or device is in an error state.</b><p>Please check device setup and try again.</p>")
  291.     return FailureUI(parent, text, title_text)
  292.  
  293. checkDeviceUi = CheckDeviceUI
  294.  
  295.  
  296. class PrinterNameValidator(QValidator):
  297.     def __init__(self, parent=None):
  298.         QValidator.__init__(self, parent)
  299.  
  300.     def validate(self, input, pos):
  301.         input = unicode(input)
  302.  
  303.         if not input:
  304.             return QValidator.Acceptable, pos
  305.  
  306.         if input[pos-1] in u"""~`!@#$%^&*()-=+[]{}()\\/,.<>?'\";:| """:
  307.             return QValidator.Invalid, pos
  308.  
  309.         # TODO: How to determine if unicode char is "printable" and acceptable
  310.         # to CUPS?
  311.         #elif input != utils.printable(input):
  312.         #    return QValidator.Invalid, pos
  313.  
  314.         return QValidator.Acceptable, pos
  315.  
  316.  
  317.  
  318. class PhoneNumValidator(QValidator):
  319.     def __init__(self, parent=None):
  320.         QValidator.__init__(self, parent)
  321.  
  322.     def validate(self, input, pos):
  323.         input = unicode(input)
  324.  
  325.         if not input:
  326.             return QValidator.Acceptable, pos
  327.  
  328.         if input[pos-1] not in u'0123456789-(+).,#* ':
  329.             return QValidator.Invalid, pos
  330.  
  331.         return QValidator.Acceptable, pos
  332.  
  333.  
  334. class AddressBookNameValidator(QValidator):
  335.     def __init__(self, db, parent=None):
  336.         QValidator.__init__(self, parent)
  337.         self.db = db
  338.  
  339.     def validate(self, input, pos):
  340.         input = unicode(input)
  341.  
  342.         if not input:
  343.             return QValidator.Acceptable, pos
  344.  
  345.         if input in self.db.get_all_names():
  346.             return QValidator.Invalid, pos
  347.  
  348.         if input[pos-1] in u'''|\\/"''': # | is the drag 'n drop separator
  349.             return QValidator.Invalid, pos
  350.  
  351.         return QValidator.Acceptable, pos
  352.  
  353.  
  354.  
  355. MIME_TYPES_DESC = \
  356. {
  357.     "application/pdf" : (__translate("PDF Document"), '.pdf'),
  358.     "application/postscript" : (__translate("Postscript Document"), '.ps'),
  359.     "application/vnd.hp-HPGL" : (__translate("HP Graphics Language File"), '.hgl, .hpg, .plt, .prn'),
  360.     "application/x-cshell" : (__translate("C Shell Script"), '.csh, .sh'),
  361.     "application/x-csource" : (__translate("C Source Code"), '.c'),
  362.     "text/cpp": (__translate("C++ Source Code"), '.cpp, .cxx'),
  363.     "application/x-perl" : (__translate("Perl Script"), '.pl'),
  364.     "application/x-python" : (__translate("Python Program"), '.py'),
  365.     "application/x-shell" : (__translate("Shell Script"), '.sh'),
  366.     "text/plain" : (__translate("Plain Text"), '.txt, .log'),
  367.     "text/html" : (__translate("HTML Dcoument"), '.htm, .html'),
  368.     "image/gif" : (__translate("GIF Image"), '.gif'),
  369.     "image/png" : (__translate("PNG Image"), '.png'),
  370.     "image/jpeg" : (__translate("JPEG Image"), '.jpg, .jpeg'),
  371.     "image/tiff" : (__translate("TIFF Image"), '.tif, .tiff'),
  372.     "image/x-bitmap" : (__translate("Bitmap (BMP) Image"), '.bmp'),
  373.     "image/x-bmp" : (__translate("Bitmap (BMP) Image"), '.bmp'),
  374.     "image/x-photocd" : (__translate("Photo CD Image"), '.pcd'),
  375.     "image/x-portable-anymap" : (__translate("Portable Image (PNM)"), '.pnm'),
  376.     "image/x-portable-bitmap" : (__translate("Portable B&W Image (PBM)"), '.pbm'),
  377.     "image/x-portable-graymap" : (__translate("Portable Grayscale Image (PGM)"), '.pgm'),
  378.     "image/x-portable-pixmap" : (__translate("Portable Color Image (PPM)"), '.ppm'),
  379.     "image/x-sgi-rgb" : (__translate("SGI RGB"), '.rgb'),
  380.     "image/x-xbitmap" : (__translate("X11 Bitmap (XBM)"), '.xbm'),
  381.     "image/x-xpixmap" : (__translate("X11 Pixmap (XPM)"), '.xpm'),
  382.     "image/x-sun-raster" : (__translate("Sun Raster Format"), '.ras'),
  383.     "application/hplip-fax" : (__translate("HPLIP Fax File"), '.g3, .g4'),
  384. }
  385.  
  386. # pixmaps for status list(s) (inkjet, laserjet)
  387. status_icons = None
  388.  
  389. def getStatusListIcon(error_state):
  390.     global status_icons
  391.     if status_icons is None:
  392.         status_icons = {
  393.           ERROR_STATE_CLEAR : (load_pixmap('idle', '16x16'), load_pixmap('idle', '16x16')),
  394.           ERROR_STATE_BUSY : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  395.           ERROR_STATE_ERROR : (load_pixmap('error', '16x16'), load_pixmap('error', '16x16')),
  396.           ERROR_STATE_LOW_SUPPLIES : (load_pixmap('inkdrop', '16x16'), load_pixmap('toner', '16x16')),
  397.           ERROR_STATE_OK : (load_pixmap('ok', '16x16'), load_pixmap('ok', '16x16')),
  398.           ERROR_STATE_WARNING : (load_pixmap('warning', '16x16'), load_pixmap('warning', '16x16')),
  399.           ERROR_STATE_LOW_PAPER: (load_pixmap('paper', '16x16'), load_pixmap('paper', '16x16')),
  400.           ERROR_STATE_PRINTING : (load_pixmap("print", '16x16'), load_pixmap("print", '16x16')),
  401.           ERROR_STATE_SCANNING : (load_pixmap("scan", '16x16'), load_pixmap("scan", '16x16')),
  402.           ERROR_STATE_PHOTOCARD : (load_pixmap("pcard", '16x16'), load_pixmap("pcard", '16x16')),
  403.           ERROR_STATE_FAXING : (load_pixmap("fax", '16x16'), load_pixmap("fax", '16x16')),
  404.           ERROR_STATE_COPYING :  (load_pixmap("makecopies", '16x16'), load_pixmap("makecopies", '16x16')),
  405.         }
  406.  
  407.     return status_icons.get(error_state, status_icons[ERROR_STATE_CLEAR])
  408.  
  409. # pixmaps for device icons (inkjet, laserjet)
  410. overlay_icons = None
  411.  
  412. def getStatusOverlayIcon(error_state):
  413.     global overlay_icons
  414.     if overlay_icons is None:
  415.         overlay_icons = {
  416.             ERROR_STATE_CLEAR : (None, None),
  417.             ERROR_STATE_BUSY : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  418.             ERROR_STATE_ERROR : (load_pixmap('error', '16x16'), load_pixmap('error', '16x16')),
  419.             ERROR_STATE_LOW_SUPPLIES : (load_pixmap('inkdrop', '16x16'), load_pixmap('toner', '16x16')),
  420.             ERROR_STATE_OK : (load_pixmap('ok', '16x16'), load_pixmap('ok', '16x16')),
  421.             ERROR_STATE_WARNING : (load_pixmap('warning', '16x16'), load_pixmap('warning', '16x16')),
  422.             ERROR_STATE_LOW_PAPER: (load_pixmap('paper', '16x16'), load_pixmap('paper', '16x16')),
  423.             ERROR_STATE_PRINTING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  424.             ERROR_STATE_SCANNING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  425.             ERROR_STATE_PHOTOCARD : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  426.             ERROR_STATE_FAXING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  427.             ERROR_STATE_COPYING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
  428.             ERROR_STATE_REFRESHING : (load_pixmap('refresh1', '16x16'), load_pixmap('refresh1', '16x16')),
  429.         }
  430.  
  431.     return overlay_icons.get(error_state, overlay_icons[ERROR_STATE_CLEAR])
  432.  
  433.  
  434. NUM_REPRS = {
  435.       1 : __translate("one"),
  436.       2 : __translate("two"),
  437.       3 : __translate("three"),
  438.       4 : __translate("four"),
  439.       5 : __translate("five"),
  440.       6 : __translate("six"),
  441.       7 : __translate("seven"),
  442.       8 : __translate("eight"),
  443.       9 : __translate("nine"),
  444.       10 : __translate("ten"),
  445.       11 : __translate("eleven"),
  446.       12 : __translate("twelve")
  447. }
  448.  
  449. UNIT_NAMES = {
  450.     "year" : (__translate("year"), __translate("years")),
  451.     "month" : (__translate("month"), __translate("months")),
  452.     "week" : (__translate("week"), __translate("weeks")),
  453.     "day" : (__translate("day"), __translate("days")),
  454.     "hour" : (__translate("hour"), __translate("hours")),
  455.     "minute" : (__translate("minute"), __translate("minutes")),
  456.     "second" : (__translate("second"), __translate("seconds")),
  457. }
  458.  
  459.  
  460. def getTimeDeltaDesc(past):
  461.     t1 = QDateTime()
  462.     t1.setTime_t(int(past))
  463.     t2 = QDateTime.currentDateTime()
  464.     delta = t1.secsTo(t2)
  465.     return __translate("(%1 ago)").arg(stringify(delta))
  466.  
  467.  
  468. # "Nicely readable timedelta"
  469. # Credit: Bjorn Lindqvist
  470. # ASPN Python Recipe 498062
  471. # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498062
  472. # Note: Modified from recipe
  473. def getSecondsInUnits(seconds):
  474.     unit_limits = [("year", 31536000),
  475.                    ("month", 2592000),
  476.                    ("week", 604800),
  477.                    ("day", 86400),
  478.                    ("hour", 3600),
  479.                    ("minute", 60)]
  480.  
  481.     for unit_name, limit in unit_limits:
  482.         if seconds >= limit:
  483.             amount = int(round(float(seconds) / limit))
  484.             return amount, unit_name
  485.  
  486.     return seconds, "second"
  487.  
  488.  
  489. def stringify(seconds):
  490.     amount, unit_name = getSecondsInUnits(seconds)
  491.  
  492.     try:
  493.         i18n_amount = NUM_REPRS[amount]
  494.     except KeyError:
  495.         i18n_amount = unicode(amount)
  496.  
  497.     if amount == 1:
  498.         i18n_unit = UNIT_NAMES[unit_name][0]
  499.     else:
  500.         i18n_unit = UNIT_NAMES[unit_name][1]
  501.  
  502.     return QString("%1 %2").arg(i18n_amount).arg(i18n_unit)
  503.  
  504.  
  505.